/*
 * Copyright 2022-2027 中国信息通信研究院云计算与大数据研究所
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */ 
package com.service.impl;

import java.math.BigDecimal;
import java.sql.Time;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.Callable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;

import com.alibaba.fastjson.JSONObject;
import com.common.calculation.AmountUtil;
import com.common.calculation.InitiUtil;
import com.common.constant.AccountConstant;
import com.common.constant.PropertiesConstant;
import com.common.context.SpringContextUtils;
import com.mapper.Transfer;
import com.pojo.Account;
import com.pojo.Branch;
import com.pojo.Customer;
import com.pojo.Sjno;
import com.pojo.Trancfg;
import com.pojo.Tranlist;
import com.pojo.Tranlog;
import com.service.TransferService;


@Service
@Scope("prototype")
public class TransferServiceImpl implements TransferService,Callable<Long> {

    private final Logger logger = LoggerFactory.getLogger(TransferServiceImpl.class); 

    @Autowired
    DataSourceTransactionManager dataSourceTransactionManager;
    @Autowired
    TransactionDefinition transactionDefinition;
    
    @Autowired
    private Transfer transfer;
    @Autowired
    private PropertiesConstant propertiesConstant;
    private int num;
    
    private int datacfg_accnum;
    private String process_id;

    private String trancfgType;
    private JSONObject json = new JSONObject();
    private Trancfg tf;

    /**
     * 单线程执行转账操作
     * @param num
     * @param datacfg_accnum
     * @throws Exception 
     */
    public Long call() throws Exception {
    	long countTime = System.currentTimeMillis();
        for (int i = 0; i < num; i++) {
    		long costTime = System.currentTimeMillis();
        	Date nowDate = new Date();
        	long nowMillis = nowDate.getTime();
    		Time nowTime = new Time(nowMillis);
    		BigDecimal money = AmountUtil.getRandomMoney(0,AccountConstant.ACCOUNT_DEFAULT_MONERY);//转账金额
            String accountFrom = "60"+String.valueOf((new Random().nextInt(datacfg_accnum)+1)+InitiUtil.getAccnum(datacfg_accnum));//借方
            String accountTo = "60"+String.valueOf((new Random().nextInt(datacfg_accnum)+1)+InitiUtil.getAccnum(datacfg_accnum));//贷方
            while(accountFrom.equals(accountTo)) {//保证借方贷方不相等
            	accountTo = "60"+String.valueOf((new Random().nextInt(datacfg_accnum)+1)+InitiUtil.getAccnum(datacfg_accnum));
            }
            if(accountFrom.compareTo(accountTo)>0) {//保证账号小的为借方
            	String temp = accountFrom;
            	accountFrom = accountTo;
            	accountTo = temp;
            }
			json.put("0_random_account", System.currentTimeMillis()-costTime);
            costTime = System.currentTimeMillis();
            Account from = null;
            Account to = null;
            try {
            	from = transfer.readAccount(accountFrom);
    			to = transfer.readAccount(accountTo);
			} catch (Exception e) {
				e.printStackTrace();
    			logger.error("TransferServiceImpl runTransfer readAccount error {} ", e);
			}
    		insertBreakPoint();
			if(from==null) {
				logger.info("TransferServiceImpl runTransfer Account from {} is null ", accountFrom);
				continue;
			}
			if(to==null) {
				logger.info("TransferServiceImpl runTransfer Account to {} is null ", accountTo);
				continue;
			}
			json.put("1_read_account", System.currentTimeMillis()-costTime);
			Tranlog tranLog = null;
			costTime = System.currentTimeMillis();
          	int fromSeq = updateAccountBranchSeq(from);
        	json.put("2_update_branch", System.currentTimeMillis()-costTime);
          	if(fromSeq==AccountConstant.ACCOUNT_SEQ_FAILURE) {//更新借方网点流水号失败
          		tranLog = new Tranlog(nowDate,from.getAccount_branchid(),fromSeq,trancfgType
            			,nowDate.getTime(),System.currentTimeMillis(),(int)(System.currentTimeMillis()-nowMillis),AccountConstant.ACCOUNT_TRAN_FAILURE,"",process_id);
    			logger.error("TransferServiceImpl runTransfer updateAccountBranchSeq from {} branch {} seq {} rollback ", accountFrom,from.getAccount_branchid(),fromSeq);
	          	continue;
          	}else {
    			logger.info("TransferServiceImpl runTransfer updateAccountBranchSeq from {} branch {} seq {} success ", accountFrom,from.getAccount_branchid(),fromSeq);
          	}
          	try {
          		 if((from.getAccount_stat().equals(AccountConstant.ACCOUNT_NORMAL)
         				||from.getAccount_stat().equals(AccountConstant.ACCOUNT_ONLYOUT))) {
             		if(to!=null&&(to.getAccount_stat().equals(AccountConstant.ACCOUNT_NORMAL)
         					||to.getAccount_stat().equals(AccountConstant.ACCOUNT_ONLYIN))) {
             			costTime = System.currentTimeMillis();
             			tranLog = updateTranMoney(accountFrom,accountTo,money,tranLog,fromSeq,nowDate,nowTime,nowMillis);//更新转账金额
             			if(tranLog==null) {//转账业务回滚
             				tranLog = new Tranlog(nowDate,from.getAccount_branchid(),fromSeq,trancfgType
                         			,nowDate.getTime(),System.currentTimeMillis(),(int)(System.currentTimeMillis()-nowMillis),AccountConstant.ACCOUNT_TRAN_FAILURE,"",process_id);
             	    		logger.error("TransferServiceImpl updateTranMoney rollback accountFrom {} accountTo {}" 
             						,accountFrom,accountTo);
             			}
             		}else {//贷方账户不正常
             			tranLog = new Tranlog(nowDate,from.getAccount_branchid(),fromSeq,trancfgType
                     			,nowDate.getTime(),System.currentTimeMillis(),(int)(System.currentTimeMillis()-nowMillis),AccountConstant.ACCOUNT_TRAN_FAILURE,"",process_id);
             			logger.info("TransferServiceImpl runTransfer Account to  {} not normal state is {} ",accountTo, to.getAccount_stat() );
             		}
                 }else {//借方账户不正常
                 	tranLog = new Tranlog(nowDate,from.getAccount_branchid(),fromSeq,trancfgType
                 			,nowDate.getTime(),System.currentTimeMillis(),(int)(System.currentTimeMillis()-nowMillis),AccountConstant.ACCOUNT_TRAN_FAILURE,"",process_id);
         			logger.info("TransferServiceImpl runTransfer Account from {} not normal state is {} " ,accountFrom, from.getAccount_stat() );
                 }
			} catch (Exception e) {
				logger.error("TransferServiceImpl updateAccountBranchSeq run error brachid {} e {}" 
						,from.getAccount_branchid(),e);
			}
        	json.put("3_update_TranMoney", System.currentTimeMillis()-costTime);
			costTime = System.currentTimeMillis();
            //插入transLog
          	insertAccountTranlog(tranLog,0);
        	json.put("4_insert_Tranlog", System.currentTimeMillis()-costTime);
        	//logger.error(json.toString());
        }
        //logger.error("耗时"+(System.currentTimeMillis()-countTime));
        return System.currentTimeMillis()-countTime;
	}

    /**
     * 插入断点
     */
    private void insertBreakPoint() {
    	if(tf!=null&&"Y".equals(tf.getTrancfg_brkpot())) {
    		try {
				Thread.sleep(Long.parseLong(tf.getTrancfg_brktime()));
			} catch (Exception e) {
		    	logger.error("TransferServiceImpl insertBreakPoint is run error e {} ",e);
			}
    	}
	}

	/**
     * 插入tranLog
     * @param tranLog
     */
    public void insertAccountTranlog(Tranlog tranLog,int repeat) {
    	TransactionStatus transactionStatus = null;
    	boolean isSuccess = false;
    	try {
    		transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
    		transfer.insertTranlog(tranLog);
    		logger.info("TransferServiceImpl insertAccountTranlog tranlog_date {} tranlog_branchid {} tranlog_branchseq {} success "
    				,tranLog.getTranlog_date(),tranLog.getTranlog_branchid(),tranLog.getTranlog_branchseq());
    		insertBreakPoint();
        	dataSourceTransactionManager.commit(transactionStatus);//提交
        	isSuccess = true;
		} catch (Exception e) {
			logger.error("TransferServiceImpl insertAccountTranlog rollback tranlog_date {} tranlog_branchid {} tranlog_branchseq {} e {}" 
					,tranLog.getTranlog_date(),tranLog.getTranlog_branchid(),tranLog.getTranlog_branchseq(),e);
			try {
		    	dataSourceTransactionManager.rollback(transactionStatus);
			} catch (Exception e2) {
				logger.error("TransferServiceImpl insertAccountTranlog rollback error accountTo {} e {}",e);
			}
		}
    	if(isSuccess) {
    		LocalTranLogServiceImpl localTranLogServiceImpl = SpringContextUtils.getBean(LocalTranLogServiceImpl.class);
        	localTranLogServiceImpl.setTranlog(tranLog);
        	localTranLogServiceImpl.start();
    	}else {
			logger.error("========>重试次数"+repeat+"===>"+tranLog.getTranlog_branchid());
    		if(repeat<propertiesConstant.getRepeat()) {
    			try {
					Thread.sleep(500);
	    	 		insertAccountTranlog(tranLog,repeat+1);
				} catch (InterruptedException e) {
					logger.error("DepositMoneyServiceImpl insertAccountTranlog Thread error tranlog_date {} tranlog_branchid {} tranlog_branchseq {} e {}" 
							,tranLog.getTranlog_date(),tranLog.getTranlog_branchid(),tranLog.getTranlog_branchseq(),e);
				}
    		}
    	}
	}

	/**
     * 更新网点流水号
     * @param account
     */
    public int updateAccountBranchSeq(Account account) {
    	TransactionStatus transactionStatus = null;
    	int seq = AccountConstant.ACCOUNT_SEQ_FAILURE;
    	try {
    		transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
    		Branch branch = transfer.readTransactionalBranchSeq(account.getAccount_branchid());
        	seq = branch.getBranch_seq();
    		transfer.updateBranch(branch.getBranch_id());//更新网点流水号
    		insertBreakPoint();
        	dataSourceTransactionManager.commit(transactionStatus);//提交
		} catch (Exception e) {
			e.printStackTrace();
			try {
				dataSourceTransactionManager.rollback(transactionStatus);
			} catch (Exception e2) {
				logger.error("TransferServiceImpl updateAccountBranchSeq rollback error account {} branchId {} e {}",account.getAccount_id(),account.getAccount_branchid(),e);
			}
	    	logger.error("TransferServiceImpl updateAccountBranchSeq rollback account {} branchId {} e {}" 
					,account.getAccount_id(),account.getAccount_branchid(),e);
	    	return AccountConstant.ACCOUNT_SEQ_FAILURE;

		}
		return seq;
	}

	/**
     * 更新转账金额
     * @param accountFrom
     * @param accountTo
     * @param money
     * @param tranLog
	 * @param toSeq 
	 * @param fromSeq 
	 * @param nowTime 
	 * @param nowDate 
	 * @param nowMillis 
	 * @param branchIdList 
	 * @return 
     */
    public Tranlog updateTranMoney(String accountFrom, String accountTo, BigDecimal money, Tranlog tranLog, int fromSeq, Date nowDate, Time nowTime, long nowMillis) {
    	TransactionStatus transactionStatus = null;
    	try {
			long costTime = System.currentTimeMillis();
    		transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
    		Account from = transfer.readTransactionalAccount(accountFrom);
    		if(from.getAccount_bale().subtract(money).doubleValue()<0) {
            	json.put("3_1_update_readTransaction", System.currentTimeMillis()-costTime);
    			logger.info("TransferServiceImpl updateTranMoney Account from  {} money {} is not enough {} ",accountFrom, from.getAccount_bale(),money );
    			tranLog = new Tranlog(nowDate,from.getAccount_branchid(),fromSeq,trancfgType
            			,nowDate.getTime(),System.currentTimeMillis(),(int)(System.currentTimeMillis()-nowMillis),AccountConstant.ACCOUNT_TRAN_FAILURE,"",process_id);
    		}else {
        		Account to = transfer.readTransactionalAccount(accountTo);
            	json.put("3_1_update_readTransaction", System.currentTimeMillis()-costTime);
            	costTime = System.currentTimeMillis();
        		//更新借方余额和借方科目余额
        		Account af = new Account();
                af.setAccount_id(accountFrom);
                af.setAccount_bale(money);
                transfer.transferFrom(af);
            	json.put("3_2_update_from_account", System.currentTimeMillis()-costTime);
        		logger.info("TransferServiceImpl updateTranMoney update account from {} monney {} success ", accountFrom,money);
            	costTime = System.currentTimeMillis();
                Sjno sf = new Sjno();
                sf.setSjno_id(from.getAccount_sjnoid());
                sf.setSjno_branchid(from.getAccount_branchid());
                sf.setSjno_bale(money);
                transfer.transferFromSjno(sf);
            	json.put("3_3_update_from_sjno", System.currentTimeMillis()-costTime);
        		logger.info("TransferServiceImpl updateTranMoney update accountSjno from {} Sjon {} monney {} success ", accountFrom,from.getAccount_sjnoid(),money);
            	costTime = System.currentTimeMillis();
        		Customer customerfrom = new Customer();
        		customerfrom.setCustomer_id(from.getAccount_custid());
        		customerfrom.setCustomer_bale(money);
        		transfer.updateCustomerFrom(customerfrom);
            	json.put("3_4_update_from_customter", System.currentTimeMillis()-costTime);
        		logger.info("TransferServiceImpl updateTranMoney update customerfrom {} monney {} success ", from.getAccount_custid(),money);
        		//更新贷方余额和贷方科目余额
            	costTime = System.currentTimeMillis();
                Account at = new Account();
                at.setAccount_id(accountTo);
                at.setAccount_bale(money);
                transfer.transferTo(at);
            	json.put("3_5_update_to_account", System.currentTimeMillis()-costTime);
        		logger.info("TransferServiceImpl updateTranMoney update accoun to {} monney {} success ", accountTo,money);
            	costTime = System.currentTimeMillis();
                Sjno tf = new Sjno();
                tf.setSjno_id(to.getAccount_sjnoid());
                tf.setSjno_branchid(to.getAccount_branchid());
                tf.setSjno_bale(money);
                transfer.transferToSjno(tf);
            	json.put("3_6_update_to_sjno", System.currentTimeMillis()-costTime);
        		logger.info("TransferServiceImpl updateTranMoney update accountSjno to {} Sjon {} monney {} success ", accountTo,to.getAccount_sjnoid(),money);
            	costTime = System.currentTimeMillis();
        		Customer customerTo = new Customer();
           		customerTo.setCustomer_id(to.getAccount_custid());
           		customerTo.setCustomer_bale(money);
        		transfer.updateCustomerTo(customerTo);
            	json.put("3_7_update_to_customer", System.currentTimeMillis()-costTime);
        		logger.info("TransferServiceImpl updateTranMoney update customerTo {} monney {} success ", to.getAccount_custid(),money);
        		//插入tranlist记录
            	costTime = System.currentTimeMillis();
        		Tranlist fromTranList = new Tranlist(nowDate,from.getAccount_branchid(), 
                		fromSeq, nowTime,from.getAccount_id(), money,
                		AccountConstant.TRANLIST_D, to.getAccount_id(),trancfgType,process_id,new BigDecimal(0));//写借方tranlist
                transfer.insertTranlist(fromTranList);
        		logger.info("TransferServiceImpl updateTranMoney insert from tranlist account {} success ", accountFrom);
                Tranlist toTranList = new Tranlist(nowDate,to.getAccount_branchid(),
                		fromSeq,nowTime,to.getAccount_id(),money,
                		AccountConstant.TRANLIST_C,from.getAccount_id(),trancfgType,process_id,new BigDecimal(0));//写贷方tranlist
                transfer.insertTranlist(toTranList);
            	json.put("3_8_update_insert_tranlist", System.currentTimeMillis()-costTime);
        		logger.info("TransferServiceImpl updateTranMoney insert to tranlist account {} success ", accountTo);
            	tranLog = new Tranlog(nowDate,from.getAccount_branchid(),fromSeq,trancfgType
            			,nowDate.getTime(),System.currentTimeMillis(),(int)(System.currentTimeMillis()-nowMillis),AccountConstant.ACCOUNT_TRAN_SUCCESS,"",process_id);
    		}
    		insertBreakPoint();
        	dataSourceTransactionManager.commit(transactionStatus);//提交
		} catch (Exception e) {
			logger.error("TransferServiceImpl updateTranMoney rollback accountFrom {} accountTo {} e {}" 
					,accountFrom,accountTo,e);
			try {
				dataSourceTransactionManager.rollback(transactionStatus);
			} catch (Exception e2) {
				logger.error("TransferServiceImpl updateTranMoney rollback error accountTo {} e {}",accountTo,e);
			}
	    	return null;
		}
    	return tranLog;

	}

	public int getNum() {
		return num;
	}

	public void setNum(int num) {
		this.num = num;
	}

	public int getDatacfg_accnum() {
		return datacfg_accnum;
	}

	public void setDatacfg_accnum(int datacfg_accnum) {
		this.datacfg_accnum = datacfg_accnum;
	}

	public String getTrancfgType() {
		return trancfgType;
	}

	public void setTrancfgType(String trancfgType) {
		this.trancfgType = trancfgType;
	}

	public String getProcess_id() {
		return process_id;
	}

	public void setProcess_id(String process_id) {
		this.process_id = process_id;
	}

	public Trancfg getTf() {
		return tf;
	}

	public void setTf(Trancfg tf) {
		this.tf = tf;
	}
	
	
}
